package com.main;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.Vector;

import com.CubicMesh;
import com.DrawObject;
import com.LineData;
import com.Plain;
import com.Point3D;
import com.Point4D;
import com.Polygon3D;
import com.PolygonMesh;
import com.Texture;
import com.ZBuffer;

public class Renderer3D {

	int XFOCUS=0;
	int YFOCUS=0;
	public static final double SCREEN_DISTANCE=300;

	public Point3D observerPoint=null;
	public double lightIntensity=1.0;
	public Point3D lightPoint=null;

	public int HEIGHT=0;
	public int WIDTH=0;

	public int POSX=0;
	public int POSY=0;

	public int MOVZ=0;
	public int MOVX=50;

	public static ZBuffer[] roadZbuffer;

	public int greenRgb= CarFrame.BACKGROUND_COLOR.getRGB();
	public int[] rgb=null;

	public static final int FRONT_VIEW=+1;
	public static final int REAR_VIEW=-1;
	public static int VIEW_TYPE=FRONT_VIEW;

	public static final int CAR_BOTTOM=-1;
	public static final int CAR_BACK=0;
	public static final int CAR_TOP=1;
	public static final int CAR_LEFT=2;
	public static final int CAR_RIGHT=3;
	public static final int CAR_FRONT=4;

	public void buildNewZBuffer() {



		for(int i=0;i<roadZbuffer.length;i++){

			roadZbuffer[i]=new ZBuffer(greenRgb,0);


		}
		int lenght=roadZbuffer.length;
		rgb = new int[lenght];	


	}

	public void buildScreen(BufferedImage buf) {

		int length=rgb.length;

		for(int i=0;i<length;i++){


			ZBuffer zb=roadZbuffer[i];
			//set
			rgb[i]=zb.getRgbColor(); 
			//clean
			zb.set(0,greenRgb);



		}	

		buf.getRaster().setDataElements( 0,0,WIDTH,HEIGHT,rgb);
		//buf.setRGB(0,0,WIDTH,HEIGHT,rgb,0,WIDTH);


	}

	/*private void cleanZBuffer() {

	for(int i=0;i<roadZbuffer.length;i++){

		roadZbuffer[i].set(0,greenRgb);

	}

}*/

	public double calculProspY( double x, double y, double z) {
		double newy0= (YFOCUS+(z-YFOCUS)*SCREEN_DISTANCE/(SCREEN_DISTANCE+y));
		return HEIGHT-newy0;
	}

	public double calculProspX(double x, double y, double z) {

		return (XFOCUS+(x-XFOCUS)*SCREEN_DISTANCE/(SCREEN_DISTANCE+y));

	}

	public double calculProspY(Point3D point) {
		return calculProspY( point.x,  point.y, point.z);
	}

	public double calculProspX(Point3D point) {
		return calculProspX( point.x,  point.y, point.z);
	}

	/**
	 * 
	 * DECOMPOSE PROJECTED TRIANGLE USING EDGE WALKING AND
	 * PERSPECTIVE CORRECT MAPPING
	 * 
	 * @param p3d
	 * @param color
	 * @param texture
	 * @param useLowResolution
	 * @param xDirection
	 * @param yDirection
	 * @param origin 
	 */
	public void decomposeTriangleIntoZBufferEdgeWalking(Polygon3D p3d,Color color,Texture texture,ZBuffer[] zbuffer, Point3D xDirection, Point3D yDirection, Point3D origin,int deltaX,int deltaY) {

		int rgbColor=color.getRGB();

		Point3D normal=Polygon3D.findNormal(p3d).calculateVersor();


		double cosin=Point3D.calculateCosin(normal,calculateTranslatedPoint(lightPoint));

		Point3D p0=new Point3D(p3d.xpoints[0],p3d.ypoints[0],p3d.zpoints[0]);
		Point3D p1=new Point3D(p3d.xpoints[1],p3d.ypoints[1],p3d.zpoints[1]);
		Point3D p2=new Point3D(p3d.xpoints[2],p3d.ypoints[2],p3d.zpoints[2]);



		//System.out.println(p3d+" "+rgbColor);

		double x0=(int)calculProspX(p0);
		double y0=(int)calculProspY(p0);
		double z0=p0.y;


		double x1=(int)calculProspX(p1);
		double y1=(int)calculProspY(p1);
		double z1=p1.y;

		double x2=(int)calculProspX(p2);
		double y2=(int)calculProspY(p2);
		double z2=p2.y;

		/*double maxX=Math.max(x0,x1);
		maxX=Math.max(x2,maxX);
		double maxY=Math.max(y0,y1);
		maxY=Math.max(y2,maxY);
		double minX=Math.min(x0,x1);
		minX=Math.min(x2,minX);
		double minY=Math.min(y0,y1);
		minY=Math.min(y2,minY);*/

		Point3D[] points=new Point3D[3];

		points[0]=new Point3D(x0,y0,z0,p0.x,p0.y,p0.z);
		points[1]=new Point3D(x1,y1,z1,p1.x,p1.y,p1.z);
		points[2]=new Point3D(x2,y2,z2,p2.x,p2.y,p2.z);


		int upper=0;
		int middle=1;
		int lower=2;

		for(int i=0;i<3;i++){

			if(points[i].y>points[upper].y)
				upper=i;

			if(points[i].y<points[lower].y)
				lower=i;

		}
		for(int i=0;i<3;i++){

			if(i!=upper && i!=lower)
				middle=i;
		}


		//double i_depth=1.0/zs;
		//UPPER TRIANGLE

		for(int j=(int) points[middle].y;j<points[upper].y;j++){


			double middlex=Point3D.foundXIntersection(points[upper],points[lower],j);
			double zs=Point3D.foundPZIntersection(points[upper],points[lower],j);
			double ys=Point3D.foundPYIntersection(points[upper],points[lower],j);
			double xs=Point3D.foundPXIntersection(points[upper],points[lower],j);

			double middlex2=Point3D.foundXIntersection(points[upper],points[middle],j);
			double ze=Point3D.foundPZIntersection(points[upper],points[middle],j);
			double ye=Point3D.foundPYIntersection(points[upper],points[middle],j);
			double xe=Point3D.foundPXIntersection(points[upper],points[middle],j);



			Point3D pstart=new Point3D(middlex,j,zs,xs,ys,zs);
			Point3D pend=new Point3D(middlex2,j,ze,xe,ye,ze);

			if(pstart.x>pend.x){

				Point3D swap= pend.clone();
				pend= pstart.clone();
				pstart=swap;

			}

			int start=(int)pstart.x;
			int end=(int)pend.x;



			double inverse=1.0/(end-start);
			double i_pstart_p_y=1.0/(SCREEN_DISTANCE+pstart.p_y);
			double i_end_p_y=1.0/(SCREEN_DISTANCE+pend.p_y);

			for(int i=start;i<end;i++){

				if(i<0 || j<0 )
					continue;

				if(i>=WIDTH || j>= HEIGHT)
					break;

				int tot=WIDTH*j+i;

				double l=(i-start)*inverse;

				double yi=1.0/((1-l)*i_pstart_p_y+l*i_end_p_y);
				ZBuffer zb=zbuffer[tot];
				if(!zb.isToUpdate(yi-SCREEN_DISTANCE))
					continue;


				double zi=((1-l)*pstart.p_z*i_pstart_p_y+l*pend.p_z*i_end_p_y)*yi;
				double xi=((1-l)*pstart.p_x*i_pstart_p_y+l*pend.p_x*i_end_p_y)*yi;    			

				yi-=SCREEN_DISTANCE;

				if(texture!=null)
					rgbColor=ZBuffer.pickRGBColorFromTexture(texture,new Point3D(xi,yi,zi),xDirection,yDirection,origin,deltaX, deltaY);
				if(rgbColor==greenRgb)
					continue;

				//System.out.println(x+" "+y+" "+tot);    			



				zb.set(yi,calculateShadowColor(cosin,rgbColor));
				//zb.update(yi,rgbColor);
			}


		}
		//LOWER TRIANGLE
		for(int j=(int) points[lower].y;j<points[middle].y;j++){

			double middlex=Point3D.foundXIntersection(points[upper],points[lower],j);
			double zs=Point3D.foundPZIntersection(points[upper],points[lower],j);
			double ys=Point3D.foundPYIntersection(points[upper],points[lower],j);
			double xs=Point3D.foundPXIntersection(points[upper],points[lower],j);

			double middlex2=Point3D.foundXIntersection(points[lower],points[middle],j);
			double ze=Point3D.foundPZIntersection(points[lower],points[middle],j);
			double ye=Point3D.foundPYIntersection(points[lower],points[middle],j);
			double xe=Point3D.foundPXIntersection(points[lower],points[middle],j);

			Point3D pstart=new Point3D(middlex,j,zs,xs,ys,zs);
			Point3D pend=new Point3D(middlex2,j,ze,xe,ye,ze);


			if(pstart.x>pend.x){


				Point3D swap= pend.clone();
				pend= pstart.clone();
				pstart=swap;

			}

			int start=(int)pstart.x;
			int end=(int)pend.x;

			double inverse=1.0/(end-start);

			double i_pstart_p_y=1.0/(SCREEN_DISTANCE+pstart.p_y);
			double i_end_p_y=1.0/(SCREEN_DISTANCE+pend.p_y);


			for(int i=start;i<end;i++){

				if(i<0 || j<0 )
					continue;

				if(i>=WIDTH || j>= HEIGHT)
					break;

				int tot=WIDTH*j+i;

				double l=(i-start)*inverse;

				double yi=1.0/((1-l)*i_pstart_p_y+l*i_end_p_y);
				ZBuffer zb=zbuffer[tot];
				if(!zb.isToUpdate(yi-SCREEN_DISTANCE))
					continue;

				double zi=((1-l)*pstart.p_z*i_pstart_p_y+l*pend.p_z*i_end_p_y)*yi;
				double xi=((1-l)*pstart.p_x*i_pstart_p_y+l*pend.p_x*i_end_p_y)*yi;



				yi-=SCREEN_DISTANCE;

				if(texture!=null)
					rgbColor=ZBuffer.pickRGBColorFromTexture(texture,new Point3D(xi,yi,zi),xDirection,yDirection,origin, deltaX,deltaY);
				if(rgbColor==greenRgb)
					continue;

				//System.out.println(x+" "+y+" "+tot);


				zb.set(yi,calculateShadowColor(cosin,rgbColor));
				//zb.update(yi,rgbColor);

			}


		}	




	}

	static float[] compArray=new float[3];

	private int calculateShadowColor(double cosin, int argbs) {

		if(false) 
			return argbs;
		else{

			double factor=(lightIntensity*(0.75+0.25*cosin));

			int alphas=0xff & (argbs>>24);
			int rs = 0xff & (argbs>>16);
			int gs = 0xff & (argbs >>8);
			int bs = 0xff & argbs;

			rs=(int) (factor*rs);
			gs=(int) (factor*gs);
			bs=(int) (factor*bs);

			return alphas <<24 | rs <<16 | gs <<8 | bs;
		}
	}

	/*public void decomposeTriangleIntoZBuffer(Polygon3D p3d,Color color,Texture texture,ZBuffer[] zbuffer, Point3D xDirection, Point3D yDirection) {


    int rgbColor=color.getRGB();

	Point3D normal=Polygon3D.findNormal(p3d).calculateVersor();

	Point3D p0=new Point3D(p3d.xpoints[0],p3d.ypoints[0],p3d.zpoints[0]);
	Point3D p1=new Point3D(p3d.xpoints[1],p3d.ypoints[1],p3d.zpoints[1]);
	Point3D p2=new Point3D(p3d.xpoints[2],p3d.ypoints[2],p3d.zpoints[2]);

	//System.out.println(p3d+" "+rgbColor);

	double x0=calculProspX(p0);
	double y0=calculProspY(p0);
	double z0=p0.y;


	double x1=calculProspX(p1);
	double y1=calculProspY(p1);
	double z1=p1.y;

	double x2=calculProspX(p2);
	double y2=calculProspY(p2);
	double z2=p2.y;

	double dx10=(x1-x0);
	double dy10=(y1-y0);
	double dz10=(z1-z0);

	double dx20=(x2-x0);
	double dy20=(y2-y0);
	double dz20=(z2-z0);

	double n_cycles= Math.max(Point2D.distance(x0,y0,x1,y1),Point2D.distance(x1,y1,x2,y2));
	n_cycles= Math.max(Point2D.distance(x0,y0,x2,y2),n_cycles);

	//far points don't need great precision !
	//n_cycles=n_cycles*SCREEN_DISTANCE/(SCREEN_DISTANCE+minY);

	if(n_cycles<1) n_cycles=1;
	n_cycles=(int)n_cycles;
	//int m_cycles=(int) Math.max(Point3D.distance(x0,y0,z0,x2,y2,z2),1);
	//double dm=1.0/m_cycles;
	//int m_cycles=(int) n_cycles;
	//double dm=1.0/m_cycles;

	double dl=1.0/n_cycles;

	for(int j=0;j<=n_cycles;j++){

		double l=j*dl;
		double min_l=(1-l);

		double dlx10=x0+l*dx10;
		double dly10=y0+l*dy10;
		double dlz10=z0+l*dz10;

		double dlx20=min_l*dx20;
		double dly20=min_l*dy20;
		double dlz20=min_l*dz20;



		int m_cycles=(int) Math.sqrt(dlx20*dlx20+dly20*dly20+dlz20*dlz20);
		if(m_cycles<1)
			m_cycles=1;
		double dm=1.0/m_cycles;


		for(int i=0;i<=m_cycles;i++){

			double m= i*dm;
            //these are the very heavy calculations !  

			double xss= dlx10+m*(dlx20);
			double yss= dly10+m*(dly20);
			if(xss<0 || xss>=WIDTH || yss<0 || yss>= HEIGHT)
	    		continue;

			double zs=dlz10+m*(dlz20);

			int xs=(int) xss;
			int ys=(int) yss;

	    	int tot=WIDTH*ys+xs;
	        //System.out.println(x+" "+y+" "+tot);

	    	ZBuffer zb=zbuffer[tot];

	    	double i_depth=1.0/zs;
	    	zb.update(i_depth,rgbColor);

		}
	} 




}*/

	public void decomposeClippedPolygonIntoZBuffer(Polygon3D p3d,Color color,Texture texture,ZBuffer[] zbuffer){

		Point3D origin=new Point3D(p3d.xpoints[0],p3d.ypoints[0],p3d.zpoints[0]);
		decomposeClippedPolygonIntoZBuffer(p3d, color, texture,zbuffer,null,null,origin,0,0);

	}

	public void decomposeClippedPolygonIntoZBuffer(Polygon3D p3d,Color color,Texture texture,ZBuffer[] zbuffer,
			Point3D xDirection,Point3D yDirection,Point3D origin,int deltaX,int deltaY){

		Polygon3D clippedPolygon=Polygon3D.clipPolygon3DInY(p3d,0);

		if(clippedPolygon.npoints==0)
			return ;

		Polygon3D[] triangles = Polygon3D.divideIntoTriangles(clippedPolygon);

		Point3D normal=Polygon3D.findNormal(p3d);

		if(!isFacing(clippedPolygon,normal,observerPoint))
			return;

		if(texture!=null && xDirection==null && yDirection==null){

			Point3D p0=new Point3D(p3d.xpoints[0],p3d.ypoints[0],p3d.zpoints[0]);
			Point3D p1=new Point3D(p3d.xpoints[1],p3d.ypoints[1],p3d.zpoints[1]);
			xDirection=(p1.substract(p0)).calculateVersor();



			yDirection=Point3D.calculateCrossProduct(normal,xDirection).calculateVersor();

			//yDirection=Point3D.calculateOrthogonal(xDirection);
		}

		for(int i=0;i<triangles.length;i++){

			decomposeTriangleIntoZBufferEdgeWalking( triangles[i],color, texture,zbuffer, xDirection,yDirection,origin, deltaX, deltaY);

		}

	}
	
	public void drawObjects(DrawObject[] drawObjects,Area totalVisibleField,ZBuffer[] zbuffer){


        Rectangle rect = totalVisibleField.getBounds();
		for(int i=0;i<drawObjects.length;i++){

			DrawObject dro=drawObjects[i];
     		drawPolygonMesh(dro, rect, zbuffer);
		}		

	}	

	private void drawPolygonMesh(DrawObject dro,Rectangle rect,ZBuffer[] zbuffer) {
	
		//if(!totalVisibleField.contains(dro.x-POSX,VIEW_DIRECTION*(dro.y-POSY)))
	
		if(rect.y+rect.height<dro.y-POSY)
			return;
		
		PolygonMesh mesh = dro.getMesh();
	    
		decomposeCubicMesh((CubicMesh) mesh,CarFrame.objectTextures[dro.getIndex()]);
		
	}
	
	public void decomposeCubicMesh(CubicMesh cm, Texture texture){
		
		
		
		Point3D xDirection=null;
		Point3D yDirection=null;		
		
		Point3D zVersor=new Point3D(0,0,1);
		Point3D zMinusVersor=new Point3D(0,0,-1);
	
		//to calculate
		Point3D xVersor0=cm.getXAxis();
		Point3D yVersor0=cm.getYAxis();
		
		Point3D xVersor=cm.getXAxis();
		Point3D yVersor=cm.getYAxis();
		
		if(VIEW_TYPE==REAR_VIEW){
			///???
			yVersor=new Point3D(-yVersor0.x,-yVersor0.y,yVersor0.z);
			xVersor=new Point3D(-xVersor0.x,-xVersor0.y,xVersor0.z);
		}
			
		for(int i=0;i<cm.polygonData.size();i++){
			
			Point3D rotateOrigin=cm.point000.clone();
			rotateOrigin=calculateTranslatedPoint(rotateOrigin);
			
			LineData ld=cm.polygonData.elementAt(i);
			Point3D normal= cm.normals.elementAt(i).clone();
			
			int deltaWidth=0;
			int deltaHeight=cm.getDeltaY();
			
			int due=(int)(255-i%15);
			Color col=new Color(due,0,0);
			
			Polygon3D polRotate=PolygonMesh.getBodyPolygon(cm.points,ld);
			int face=findBoxFace(normal,xVersor0,yVersor0,zVersor);
			polRotate=calculateTranslated3D(polRotate);
			
		
			
            //System.out.println(i+" "+face);
			if(face==CAR_BOTTOM )
				continue;
			if(face==CAR_FRONT){
				
				rotateOrigin=cm.point011.clone();
				rotateOrigin=calculateTranslatedPoint(rotateOrigin);
				
				 deltaWidth=cm.getDeltaX();
				 deltaHeight=cm.getDeltaY2();
				 xDirection=xVersor;
				 yDirection=zMinusVersor;


			}
			else if(face==CAR_BACK){
				 deltaWidth=cm.getDeltaX();
				 deltaHeight=0;
				 xDirection=xVersor;
				 yDirection=zVersor;


			}
			else if(face==CAR_TOP){
				 deltaWidth=cm.getDeltaX();
				 xDirection=xVersor;
				 yDirection=yVersor;


			}
			else if(face==CAR_LEFT) {
				
				xDirection=zVersor;
				yDirection=yVersor;
				

	
	
			}
			else if(face==CAR_RIGHT) {
				
				xDirection=zMinusVersor;
				yDirection=yVersor;
				

				rotateOrigin=cm.point001.clone();
				rotateOrigin=calculateTranslatedPoint(rotateOrigin);
				
				deltaWidth=cm.getDeltaX2();
			}
			
			
			//
			decomposeClippedPolygonIntoZBuffer(polRotate,col,texture,roadZbuffer,xDirection,yDirection,rotateOrigin,deltaWidth,deltaHeight);
				
		}
		
	
	}

	public static boolean isFacing(Polygon3D pol,Point3D normal,Point3D observer){



		//Point3D p0=new Point3D(pol.xpoints[0],pol.ypoints[0],pol.zpoints[0]);
		Point3D p0=Polygon3D.findCentroid(pol);

		Point3D vectorObs=observer.substract(p0);

		double cosin=Point3D.calculateCosin(normal,vectorObs);

		return cosin>=0;
	}

	public void decomposeObject2D(Texture image,double x,double y,double z,double dx,double dy,double dz,ZBuffer[] zbuffer){



		double drx=dx/image.getWidth();
		double drz=dz/image.getHeight();
		double deltaz=z+dz;
		int h=image.getHeight();
		int w=image.getWidth();


		for(int i=0;i<w;i++)
			for(int j=0;j<h;j++){


				int argb = image.getRGB(i, j);
				if(greenRgb==argb)
					continue;
				int alpha=0xff & (argb>>24);


				double xs=x+i*drx;
				double ys=y;
				double zs=deltaz-j*drz;

				decomposePointIntoZBuffer(xs,ys,zs,0,argb,zbuffer);


			}


	}
	
	/*public void drawObject2D(DrawObject dro,Area totalVisibleField,ZBuffer[] zbuffer){


	if(!totalVisibleField.contains(dro.x-POSX,VIEW_DIRECTION*(dro.y-POSY)))
		return;

	int index=dro.getIndex();

	if(index<0 || index> CarFrame.objects.length)
		return;

	decomposeObject2D(CarFrame.objects[index],dro.x-POSX,VIEW_DIRECTION*(dro.y-POSY),dro.z+MOVZ,dro.dx,dro.dy,dro.dz, zbuffer);

}

public void drawObject3D(DrawObject dro,Area totalVisibleField,ZBuffer[] zbuffer){

	int index=dro.getIndex();

	if(index<0 || index> CarFrame.objects.length)
		return;

	Rectangle rect = totalVisibleField.getBounds();
	
	for(int j=0;j<dro.getPolygons().size();j++){

		Polygon3D polig=(Polygon3D) dro.getPolygons().elementAt(j);
		Polygon3D translatedPolygon=calculateTranslated3D(polig);

        if(!isVisible(rect,translatedPolygon))
        	continue;

		decomposeClippedPolygonIntoZBuffer(translatedPolygon,Color.RED,CarFrame.objects[index],zbuffer);		
	
	}	


}*/

	private void decomposePointIntoZBuffer(double xs, double ys, double zs,
			double ds, int rgbColor,ZBuffer[] zbuffer) {

		if(ys<=-SCREEN_DISTANCE) return;

		//double gamma=SCREEN_DISTANCE/(SCREEN_DISTANCE+ys);

		double xd=calculProspX(xs,ys,zs);
		double yd=calculProspY(xs,ys,zs);

		if(xd<0 || xd>=WIDTH || yd<0 || yd>= HEIGHT)
			return;

		int x=(int) xd;
		int y=(int) yd;

		//double i_ys=1.0/ys;

		int tot=y*WIDTH+x;	

		zbuffer[tot].update(ys,rgbColor);




	}

	public void buildRectanglePolygons(Vector polygons, double x, double y,
			double z, double dx, double dy, double dz) {



		Polygon3D base=buildRectangleBase3D(x,y,z,dx,dy,dz);
		polygons.add(base);		
		Polygon3D ubase=base.buildTranslatedPolygon(0,0,dz);
		polygons.add(ubase);	
		polygons.add(Polygon3D.buildPrismIFace(ubase,base,0));
		polygons.add(Polygon3D.buildPrismIFace(ubase,base,1));
		polygons.add(Polygon3D.buildPrismIFace(ubase,base,2));
		polygons.add(Polygon3D.buildPrismIFace(ubase,base,3));
	}


	public Polygon3D buildRectangleBase3D(double x,double y,double z,double dx,double dy,double dz){

		int[] cx=new int[4];
		int[] cy=new int[4];
		int[] cz=new int[4];

		cx[0]=(int) x;
		cy[0]=(int) y;
		cz[0]=(int) z;
		cx[1]=(int) (x+dx);
		cy[1]=(int) y;
		cz[1]=(int) z;
		cx[2]=(int) (x+dx);
		cy[2]=(int) (y+dy);
		cz[2]=(int) z;
		cx[3]=(int) x;
		cy[3]=(int) (y+dy);
		cz[3]=(int) z;

		Polygon3D base=new Polygon3D(4,cx,cy,cz);

		return base;

	}

	public Point3D calculateTranslatedPoint(Point3D point) {

		Point3D newPoint=new Point3D();

		if(VIEW_TYPE==FRONT_VIEW){
			newPoint.x=point.x-POSX;
			newPoint.y=(point.y-POSY);
			newPoint.z=point.z+MOVZ;
		}
		else{

			newPoint.x=2*XFOCUS-(point.x-POSX);
			newPoint.y=-(point.y-POSY);
			newPoint.z=point.z+MOVZ;
		}
		return newPoint;
	}
	
	private Polygon3D calculateTranslated3D(Polygon3D base) {

		Polygon3D translatedBase=new Polygon3D(base.npoints);

		for(int i=0;i<base.npoints;i++){

			if(VIEW_TYPE==FRONT_VIEW){
				translatedBase.xpoints[i]=base.xpoints[i]-POSX;
				translatedBase.ypoints[i]=(base.ypoints[i]-POSY);
				translatedBase.zpoints[i]=base.zpoints[i]+MOVZ;
			}
			else{
				
				translatedBase.xpoints[i]=2*XFOCUS-(base.xpoints[i]-POSX);
				translatedBase.ypoints[i]=-(base.ypoints[i]-POSY);
				translatedBase.zpoints[i]=base.zpoints[i]+MOVZ;
			}
		}

		return translatedBase;
	}
	
	public void rotate(){
		// TODO Auto-generated method stub
	}
	
	
	public void rotatePolygon(DrawObject dro,double xo,double yo,double cosTur,double sinTur){
		
		for(int j=0;j<dro.getPolygons().size();j++){
			
			Polygon3D polig=(Polygon3D) dro.getPolygons().elementAt(j);
			rotatePolygon( polig, xo, yo, cosTur, sinTur);
		}
		
		
	}

	public void rotatePolygon(Polygon3D polig,double xo,double yo,double cosTur,double sinTur){



		for(int i=0;i<polig.npoints;i++){

			int x=polig.xpoints[i];
			int y=polig.ypoints[i];	

			polig.xpoints[i]=(int)(xo+(x-xo)*cosTur-(y-yo)*sinTur);
			polig.ypoints[i]=(int)(yo+(y-yo)*cosTur+(x-xo)*sinTur);

		}


	}
	
	public void rotateMesh(CubicMesh mesh, double xo, double yo, double ct,
			double st) {
		
		for(int i=0;i<mesh.points.length;i++){
			
			rotatePoint(mesh.points[i],xo,yo,ct,st);

		}
		
		for(int i=0;i<mesh.normals.size();i++){
			
			
			Point3D normal = mesh.normals.elementAt(i);
			rotatePoint(normal,0,0,ct,st);

		}
		
		rotatePoint(mesh.point000,xo,yo,ct,st);
		rotatePoint(mesh.point100,xo,yo,ct,st);
		rotatePoint(mesh.point010,xo,yo,ct,st);
		rotatePoint(mesh.point001,xo,yo,ct,st);
		rotatePoint(mesh.point110,xo,yo,ct,st);
		rotatePoint(mesh.point011,xo,yo,ct,st);
		rotatePoint(mesh.point101,xo,yo,ct,st);
		rotatePoint(mesh.point111,xo,yo,ct,st);
		
	}
	
	public void rotatePoint(Point3D p,double xo,double yo,double cosTur,double sinTur){


			double x=p.x;
			double y=p.y;	

			p.x=xo+(x-xo)*cosTur-(y-yo)*sinTur;
			p.y=yo+(y-yo)*cosTur+(x-xo)*sinTur;

	

	}

	public Polygon3D interpolateAreaToPolygon3D(Area a, Polygon3D p3d){




		Vector points=new Vector();

		PathIterator pathIter = a.getPathIterator(null);
		//if(isDebug)System.out.println(p3d);
		while(!pathIter.isDone()){

			double[] coords = new double[6];

			int type=pathIter.currentSegment(coords);

			double px= coords[0];
			double py= coords[1];	



			if(type==PathIterator.SEG_MOVETO || type==PathIterator.SEG_LINETO)
			{		//here i could use the 3d polygon to interpolate the z value ?


				double pz=interpolate(px,py,p3d);
				points.add(new Point4D(px,py,pz));

			}
			pathIter.next();
		}

		Polygon3D draw_p=new Polygon3D(points);
		return draw_p;

	}
	
	protected double interpolate(double px, double py, Polygon3D p3d) {
       
		/*Plain plain=Plain.calculatePlain(p3d);
		return plain.calculateZ(px,py);

		 */
		Polygon3D p1=Polygon3D.extractSubPolygon3D(p3d,3,0);

		if(p1.hasInsidePoint(px,py)){

			Plain plane1=Plain.calculatePlain(p1);

			return plane1.calculateZ(px,py);

		}

		Polygon3D p2=Polygon3D.extractSubPolygon3D(p3d,3,2);

		if(p2.hasInsidePoint(px,py)){

			Plain plane2=Plain.calculatePlain(p2);

			return plane2.calculateZ(px,py);

		}


		return 0;
	}

	public static int findBoxFace(Point3D normal,Point3D versusx,Point3D versusy,Point3D versusz) {

		double normx=Point3D.calculateDotProduct(normal,versusx);
		double normy=Point3D.calculateDotProduct(normal,versusy);
		double normz=Point3D.calculateDotProduct(normal,versusz);

		double absx = Math.abs(normx);
		double absy = Math.abs(normy);
		double absz = Math.abs(normz);

		if(absx>=absy && absx>=absz ){

			if(normx>0)
				return CAR_RIGHT;
			else
				return CAR_LEFT;

		}
		if(absy>=absx && absy>=absz ){

			if(normy>0)
				return CAR_FRONT;
			else
				return CAR_BACK;

		}
		if(absz>=absx && absz>=absy ){

			if(normz>0)
				return CAR_TOP;
			else
				return CAR_BOTTOM;

		}

		return -1;
	}

	public static int findBoxFace(Polygon3D pol) {

		Point3D normal = Polygon3D.findNormal(pol);
		return findBoxFace(normal);

	}

	public static int findBoxFace(Point3D normal ) {

		return findBoxFace(normal,new Point3D(1,0,0),new Point3D(0,1,0),new Point3D(0,0,1));
	}
	

	public void left(Graphics2D graphics2D) {


		POSX=POSX-MOVX;


	}

	public void right(Graphics2D graphics2D) {


		POSX=POSX+MOVX;

	}
	
	public int getMOVX() {
		return MOVX;
	}

	public int getMOVZ() {
		return MOVZ;
	}

	public void setPOSX(int posx) {
		POSX = posx;
	}

	public static void showMemoryUsage() {

		MemoryMXBean xbean = ManagementFactory.getMemoryMXBean();
		MemoryUsage heap=xbean.getHeapMemoryUsage();
		MemoryUsage nonheap=xbean.getNonHeapMemoryUsage();
		Runtime run=Runtime.getRuntime();

		System.out.println("free:"+run.freeMemory()+" , total: "+run.totalMemory());

		System.out.println("heap:"+heap);
		System.out.println("non heap:"+nonheap);
		System.out.println("*************");
	}

}
